package name.huzhenbo.java.objects;

/**
 * So when is it appropriate to override Object.equals? When a class has a
 * notion of logical equality that differs from mere object identity, and a superclass
 * has not already overridden equals to implement the desired behavior. This is gen-
 * erally the case for value classes. A value class is simply a class that represents a
 * value, such as Integer or Date. A programmer who compares references to value
 * objects using the equals method expects to find out whether they are logically
 * equivalent, not whether they refer to the same object. Not only is overriding the
 * equals method necessary to satisfy programmer expectations; it enables instances
 * to serve as map keys or set elements with predictable, desirable behavior.
 */
class AutoImplementEquals {
    private int i;
    private long l;
    private float f;
    private double d;
    private String s;
    private ImplementsValidEquals o;

    public AutoImplementEquals(int i, long l, float f, double d, String s, ImplementsValidEquals o) {
        this.i = i;
        this.l = l;
        this.f = f;
        this.d = d;
        this.s = s;
        this.o = o;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }

        // ensure same class, avoids compares over different hierancy
        if (getClass() != obj.getClass()) {
            return false;
        }

        // Cast the object.
        final AutoImplementEquals other = (AutoImplementEquals) obj;

        if (this.i != other.i) {
            return false;
        }
        if (this.l != other.l) {
            return false;
        }
        if (this.f != other.f) {
            return false;
        }
        if (this.d != other.d) {
            return false;
        }
        // checks by == first, this will increase the performance
        if (this.s != other.s && (this.s == null || !this.s.equals(other.s))) {
            return false;
        }
        if (this.o != other.o && (this.o == null || !this.o.equals(other.o))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        // prime is preferable
        int hash = 7;
        // for integer, just take it as the hashvalue.
        hash = 97 * hash + this.i;
        // long
//        System.out.println("long");
//        System.out.println((int) (this.l ^ (this.l >>> 32)));
        hash = 97 * hash + (int) (this.l ^ (this.l >>> 32));
        // float
//        System.out.println("float");
        // Float and int are both 32 bits.
//        System.out.println(Float.floatToIntBits(this.f));
        hash = 97 * hash + Float.floatToIntBits(this.f);
        // double
//        System.out.println("double");
        // Double and long are both 64 bits.
//        System.out.println(Double.doubleToLongBits(this.d));
//        System.out.println(Double.doubleToLongBits(this.d) >>> 32);
//        System.out.println((int) (Double.doubleToLongBits(this.d) ^ (Double.doubleToLongBits(this.d) >>> 32)));
        hash = 97 * hash + (int) (Double.doubleToLongBits(this.d) ^ (Double.doubleToLongBits(this.d) >>> 32));
        // object, return 0 if it's null
//        System.out.println("string");
//        System.out.println(s.hashCode());
        hash = 97 * hash + (this.s != null ? this.s.hashCode() : 0);
        hash = 97 * hash + (this.o != null ? this.o.hashCode() : 0);
        return hash;
    }


}
